package com.rajaev.dtu.tractor.domain.repository.redis;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.rajaev.dtu.tractor.domain.entity.BatteryPo;
import com.rajaev.dtu.tractor.domain.entity.Protocol485Dto;
import com.rajaev.dtu.tractor.infrastructure.annotations.SNotiCollectionField;
import com.rajaev.dtu.tractor.infrastructure.annotations.SNotiFiled;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@Repository
public class DeviceMonitorRedisRepository {

    @Autowired
    RedisTemplate redisTemplate;

    private static String KEY_PREFIX = "DEVICE:MONITOR";

    private static String tickTimeField = "tickTime";

    public void saveDeviceMonitor(BatteryPo batteryPo, Protocol485Dto protocol485Dto) {
        Map<String, String> map = new HashMap<>();
        ReflectionUtils.doWithFields(Protocol485Dto.class, field -> {
            if (field.isAnnotationPresent(SNotiFiled.class)) {
                field.setAccessible(true);
                Object o = field.get(protocol485Dto);
                if (o != null) {
                    if(o instanceof  BigDecimal){
                        map.put(field.getName(), NumberUtil.round((BigDecimal)o,2, RoundingMode.HALF_DOWN).toPlainString());
                    }else {
                        map.put(field.getName(), String.valueOf(o));
                    }

                }
            }
        });

        //设备第一次上报数据的时候没有数据
        BigDecimal[] voltagesInRedis = getBatteryVoltage(batteryPo.getVin()).stream().map(Protocol485Dto.BatteryVoltage::getDtdy).toArray(BigDecimal[]::new);
        Integer[] batteryArrayInRedis = getBatteryTemperature(batteryPo.getVin()).stream().map(Protocol485Dto.BatteryTemperature::getDcwd).toArray(Integer[]::new);


        ReflectionUtils.doWithFields(Protocol485Dto.class, field -> {
            if (field.isAnnotationPresent(SNotiCollectionField.class)) {
                // 电压
                if (field.getAnnotation(SNotiCollectionField.class).type().equals(SNotiCollectionField.Type.VOLTAGE)) {
                    List<Protocol485Dto.BatteryVoltage> batteryVoltageList = protocol485Dto.getBatteryVoltageList();
                    if (CollectionUtils.isEmpty(batteryVoltageList)) {
                        return;
                    }
                    BigDecimal[] batteryVoltageArray;
                    if (ArrayUtil.isEmpty(voltagesInRedis)) {
                        batteryVoltageArray = ArrayUtil.newArray(BigDecimal.class, batteryPo.getCellNum());

                        Arrays.fill(batteryVoltageArray, new BigDecimal(0));
                    } else {
                        batteryVoltageArray = voltagesInRedis;
                    }
                    for (Protocol485Dto.BatteryVoltage batteryVoltage : batteryVoltageList) {
                        int bh = batteryVoltage.getDtbh();
                        batteryVoltageArray[++bh] = batteryVoltage.getDtdy();
                    }

                    map.put(field.getName(), StringUtils.join(Arrays.stream(batteryVoltageArray).map(BigDecimal::doubleValue).collect(Collectors.toList()), ","));

                }
                //温度
                else if (field.getAnnotation(SNotiCollectionField.class).type().equals(SNotiCollectionField.Type.TEMPERATURE)) {
                    List<Protocol485Dto.BatteryTemperature> batteryTemperatureList = protocol485Dto.getBatteryTemperatureList();
                    if (CollectionUtils.isEmpty(batteryTemperatureList)) {
                        return;
                    }
                    Integer[] batterTemperatureArray;
                    if (ArrayUtil.isEmpty(batteryArrayInRedis)) {
                        batterTemperatureArray = ArrayUtil.newArray(Integer.class, batteryPo.getTemperatureNum());
                        Arrays.fill(batterTemperatureArray, 0);

                    } else {
                        batterTemperatureArray = batteryArrayInRedis;
                    }
                    for (Protocol485Dto.BatteryTemperature batteryTemperature : batteryTemperatureList) {
                        int bh = batteryTemperature.getWdbh();
                        batterTemperatureArray[++bh] = batteryTemperature.getDcwd();
                    }
                    map.put(field.getName(), StringUtils.join(batterTemperatureArray, ","));
                }
            }

        });
        map.put(tickTimeField, String.valueOf(protocol485Dto.getTickTime()));
        String key = getRedisKey(batteryPo.getVin());
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 根据vin获取设备的监控信息
     *
     * @param vin
     * @return
     */
    public Protocol485Dto getDeviceMonitor(String vin) {
        List<String> hks = Arrays.stream(Protocol485Dto.class.getDeclaredFields())
                .filter(field -> field.isAnnotationPresent(SNotiFiled.class))
                .map(field -> field.getName()).collect(Collectors.toList());
        String key = getRedisKey(vin);

        List<Object> valList = redisTemplate.opsForHash().multiGet(key, hks);
        Protocol485Dto protocol485Dto = new Protocol485Dto();
        for (int i = 0; i < hks.size(); i++) {
            int finalI = i;
            ReflectionUtils.doWithFields(Protocol485Dto.class, field -> {
                if (field.getName().equalsIgnoreCase(hks.get(finalI))) {
                    Object val = valList.get(finalI);
                    if (null != val) {
                        ReflectUtil.setFieldValue(protocol485Dto, field, val);
                    }
                }
            });
        }
        if (redisTemplate.opsForHash().get(key, tickTimeField) == null) {
            return new Protocol485Dto();
        }
        protocol485Dto.setTickTime(Convert.toLong(redisTemplate.opsForHash().get(key, tickTimeField)) );
        protocol485Dto.setBatteryVoltageList(getBatteryVoltage(vin));
        protocol485Dto.setBatteryTemperatureList(getBatteryTemperature(vin));
        return protocol485Dto;
    }


    /**
     * 根据vin获取电池的实时单体电压信息
     *
     * @param vin
     * @return
     */
    public List<Protocol485Dto.BatteryVoltage> getBatteryVoltage(String vin) {
        String hk = Arrays.stream(Protocol485Dto.class.getDeclaredFields())
                .filter(field ->
                        field.isAnnotationPresent(SNotiCollectionField.class) && field.getAnnotation(SNotiCollectionField.class).type().equals(SNotiCollectionField.Type.VOLTAGE))
                .findFirst().get().getName();
        String key = getRedisKey(vin);

        Object v = redisTemplate.opsForHash().get(key, hk);
        if (v == null) {
            return Collections.emptyList();
        } else {
            String[] voltageValues = String.valueOf(v).split(StrUtil.COMMA);
            List<Protocol485Dto.BatteryVoltage> list = new ArrayList<>();
            for (int i = 0; i < voltageValues.length; i++) {
                Protocol485Dto.BatteryVoltage batteryVoltage = new Protocol485Dto().new BatteryVoltage();
                batteryVoltage.setDtbh(i+1);
                batteryVoltage.setDtdy(new BigDecimal(voltageValues[i]));
                list.add(batteryVoltage);
            }
            return list;
        }
    }

    /**
     * 根据vin获取设备的实时单元温度信息
     *
     * @param vin
     * @return
     */
    public List<Protocol485Dto.BatteryTemperature> getBatteryTemperature(String vin) {
        String hk = Arrays.stream(Protocol485Dto.class.getDeclaredFields())
                .filter(field ->
                        field.isAnnotationPresent(SNotiCollectionField.class)
                                && field.getAnnotation(SNotiCollectionField.class).type().equals(SNotiCollectionField.Type.TEMPERATURE))
                .findFirst().get().getName();
        String key = getRedisKey(vin);

        Object v = redisTemplate.opsForHash().get(key, hk);
        if (v == null) {
            return Collections.emptyList();
        } else {
            String[] temperatures = String.valueOf(v).split(",");
            List<Protocol485Dto.BatteryTemperature> list = new ArrayList<>();
            for (int i = 0; i < temperatures.length; i++) {
                Protocol485Dto.BatteryTemperature temperature = new Protocol485Dto().new BatteryTemperature();
                temperature.setWdbh(i+1);
                temperature.setDcwd(Integer.valueOf(temperatures[i]));
                list.add(temperature);
            }
            return list;
        }
    }

    private String getRedisKey(String deviceNo) {
        return KEY_PREFIX.concat(StrUtil.COLON).concat(deviceNo);
    }


    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Protocol485Dto protocol485Dto = new Protocol485Dto();

        Object o = "3";
        //ReflectUtil.setFieldValue(protocol485Dto, Protocol485Dto.class.getDeclaredField("dcdtzs"),"3");
        Field field = Protocol485Dto.class.getDeclaredField("dcdtzs");
        field.setAccessible(true);
        Constructor con =  field.getType().getConstructor(o.getClass());

        ReflectUtil.setFieldValue(protocol485Dto,field,con.newInstance(o));
        System.out.println(protocol485Dto.getDcdtzs());

    }

}
